/**
 * This file is Copyright © 2008 Software Craftsmen Limited. All Rights Reserved.
 */
package com.softwarecraftsmen.generators;

import org.jetbrains.annotations.NotNull;

import static java.lang.String.format;
import static java.util.Locale.UK;

public class Range<Long> extends AbstractGenerator<Long>
{
	private final long lowerBound;
	private final long upperBound;
	private final long step;

	private final boolean isNegativeStep;
	private final boolean isPositiveStep;
	private boolean isMute;
	private long nextValue;

	@SuppressWarnings({"RedundantIfStatement"})
	private Range(final long lowerBound, final long upperBound, final long step)
	{
		isNegativeStep = step < 0;
		isPositiveStep = step > 0;
		if (upperBound > lowerBound && isNegativeStep)
		{
			isMute = true;
		}
		else if (upperBound < lowerBound && isPositiveStep)
		{
			isMute = true;
		}
		else if (step == 0)
		{
			isMute = true;
		}
		else if (upperBound == lowerBound)
		{
			isMute = true;
		}
		else
		{
			isMute = false;
		}
		this.lowerBound = lowerBound;
		this.upperBound = upperBound;
		this.step = step;
		nextValue = lowerBound;
	}

	public boolean hasNext()
	{
		return !isMute;
	}

	@NotNull
	public Long next()
	{
		guardNext();

		final long valueToReturn = nextValue;
		nextValue = lowerBound + step;
		if ((isPositiveStep && nextValue >= upperBound) || (isNegativeStep && nextValue <= lowerBound))
		{
			isMute = true;
		}
		return (Long) valueToReturn;
	}

	@NotNull
	public String toString()
	{
		return format(UK, "Range(%1$s, %2$s, %3$s)", lowerBound, upperBound, step);
	}

	/*
range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
	 */
	@NotNull
	public static Range range(final long size)
	{
		return range(0, size);
	}

	/*
range(1, 5)
[1, 2, 3, 4]
	 */
	@NotNull
	public static Range range(final long startIndex, final long excludingStopIndex)
	{
		return range(startIndex, excludingStopIndex, 1);
	}

	/*
range(1, 10, 2)
[1, 3, 5, 7, 9]

range(20, 4, -5)
[20, 15, 10, 5]

range(10, 20, -5)
[]
	*/
	@NotNull
	public static Range range(final long startIndex, final long excludingStopIndex, final long stepSize)
	{
		return new Range(startIndex, excludingStopIndex, stepSize);
	}
}
